using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using RAG.Infrastructure;
using RAG.Application;
using RAG.Application.Dtos.Auth;
using System.Text;
using System.Text.Json.Serialization;
using System.Net.WebSockets;
using System.Text.Json;
using RAG.Application.Services.Interfaces;

var builder = WebApplication.CreateBuilder(args);

// 配置日志
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.SetMinimumLevel(LogLevel.Information);

// 注册HttpContextAccessor
builder.Services.AddHttpContextAccessor();

// Add services to the container.
builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        // 处理循环引用问题
        options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
        options.JsonSerializerOptions.WriteIndented = true;
        // 使用 camelCase，便于前端直接用小写开头字段（documents, totalCount等）
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
    });

// 注册 CORS 策略
builder.Services.AddCors(options =>
{
    options.AddDefaultPolicy(policy =>
    {
        policy.WithOrigins(
                "http://localhost:5173",  // Vite 默认端口
                "http://localhost:3000",  // 其他可能的端口
                "http://localhost:8080",  // Vue CLI 默认端口
                "http://127.0.0.1:5173", // 本地 IP
                "http://127.0.0.1:3000",
                "http://127.0.0.1:8080"
            )
            .AllowAnyMethod()
            .AllowAnyHeader()
            .AllowCredentials();
    });
});

// 配置JWT认证
var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();
if (jwtSettings != null)
{
    builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options =>
        {
            options.TokenValidationParameters = new TokenValidationParameters
            {
                ValidateIssuer = true,
                ValidateAudience = true,
                ValidateLifetime = true,
                ValidateIssuerSigningKey = true,
                ValidIssuer = jwtSettings.Issuer,
                ValidAudience = jwtSettings.Audience,
                IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(jwtSettings.SecretKey))
            };
        });
}

// 添加应用层服务
builder.Services.AddApplicationServices(builder.Configuration);

// 添加基础设施层服务
builder.Services.AddInfrastructureServices(builder.Configuration);

// 内存缓存: 用于短问题 embedding 缓存
builder.Services.AddMemoryCache(options =>
{
    options.SizeLimit = 5_000_000; // 逻辑条目容量限制（embedding.Length 作为大小）
});

// 配置 OpenAPI（Swagger）服务
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// 输出启动信息
var logger = app.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("=== RAG AI 应用系统启动 ===");
logger.LogInformation("领域事件系统已启用，事件日志将显示在控制台中");

// 确保上传目录存在
var uploadPath = builder.Configuration["FileStorage:UploadPath"] ?? "uploads";
Directory.CreateDirectory(uploadPath);
logger.LogInformation("文件存储目录已配置: {UploadPath}", uploadPath);

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

// 启用 CORS - 必须在 UseAuthentication 之前
app.UseCors();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

// 启用 WebSocket 支持
app.UseWebSockets(new WebSocketOptions
{
    KeepAliveInterval = TimeSpan.FromSeconds(30)
});

// RAG 问答 WebSocket 端点: /ws/rag
app.Map("/ws/rag", async context =>
{
    if (!context.WebSockets.IsWebSocketRequest)
    {
        context.Response.StatusCode = 400;
        await context.Response.WriteAsync("WebSocket request expected");
        return;
    }

    using var webSocket = await context.WebSockets.AcceptWebSocketAsync();
    var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
    logger.LogInformation("[WS][RAG] 连接建立 {RemoteIp}", context.Connection.RemoteIpAddress);

    async Task SendAsync(object payload)
    {
        var json = JsonSerializer.Serialize(payload);
        var bytes = Encoding.UTF8.GetBytes(json);
        await webSocket.SendAsync(bytes, WebSocketMessageType.Text, true, CancellationToken.None);
    }

    var buffer = new byte[8192];
    var received = new ArraySegment<byte>(buffer);
    while (webSocket.State == WebSocketState.Open)
    {
        WebSocketReceiveResult result;
        using var ms = new MemoryStream();
        do
        {
            result = await webSocket.ReceiveAsync(received, CancellationToken.None);
            if (result.MessageType == WebSocketMessageType.Close)
            {
                await webSocket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Closing", CancellationToken.None);
                logger.LogInformation("[WS][RAG] 客户端关闭");
                return;
            }
            ms.Write(received.Array!, received.Offset, result.Count);
        } while (!result.EndOfMessage);

        ms.Seek(0, SeekOrigin.Begin);
        string incomingJson = Encoding.UTF8.GetString(ms.ToArray());
        if (string.IsNullOrWhiteSpace(incomingJson))
        {
            await SendAsync(new { type = "error", message = "Empty message" });
            continue;
        }

        JsonDocument doc;
        try
        {
            doc = JsonDocument.Parse(incomingJson);
        }
        catch (Exception ex)
        {
            await SendAsync(new { type = "error", message = "Invalid JSON", detail = ex.Message });
            continue;
        }

        var root = doc.RootElement;
        var msgType = root.GetProperty("type").GetString();
        if (msgType == "ping")
        {
            await SendAsync(new { type = "pong", ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() });
            continue;
        }
        if (msgType != "question")
        {
            await SendAsync(new { type = "error", message = "Unsupported type" });
            continue;
        }

        var question = root.GetProperty("question").GetString() ?? string.Empty;
        var knowledgeLimit = root.TryGetProperty("knowledgeLimit", out var kl) ? kl.GetInt32() : 5;
        var similarityThreshold = root.TryGetProperty("similarityThreshold", out var st) ? st.GetDouble() : 0.7;
        var allowDirect = root.TryGetProperty("allowDirectAnswer", out var ad) && ad.GetBoolean();
        
        // 获取用户ID和会话ID（从WebSocket请求中）
        var userId = root.TryGetProperty("userId", out var uid) && Guid.TryParse(uid.GetString(), out var parsedUserId) 
            ? parsedUserId 
            : Guid.Empty;
        var sessionId = root.TryGetProperty("sessionId", out var sid) && Guid.TryParse(sid.GetString(), out var parsedSessionId) 
            ? parsedSessionId 
            : (Guid?)null;

        if (string.IsNullOrWhiteSpace(question))
        {
            await SendAsync(new { type = "error", message = "Question is empty" });
            continue;
        }

        await SendAsync(new { type = "ack", question });

        try
        {
            // 构造 DTO
            var dto = new RAG.Application.Dtos.Rag.RagQuestionAnswerRequestDto
            {
                Question = question,
                UserId = userId,
                SessionId = sessionId,
                KnowledgeLimit = knowledgeLimit,
                SimilarityThreshold = similarityThreshold,
                AllowDirectAnswer = allowDirect
            };

            using var scope = context.RequestServices.CreateScope();
            var rag = scope.ServiceProvider.GetRequiredService<IRagQuestionAnswerService>();
            var chatService = scope.ServiceProvider.GetRequiredService<RAG.Domain.Services.IChatService>();
            var conversationService = scope.ServiceProvider.GetRequiredService<IConversationService>();
            var sessionService = scope.ServiceProvider.GetRequiredService<ISessionService>();
            var sw = System.Diagnostics.Stopwatch.StartNew();

            var prep = await rag.PrepareStreamingAsync(dto);
            var sourcesCompact = prep.KnowledgeSources.Select(s => new { s.Id, s.Title, s.SimilarityScore }).ToList();
            await SendAsync(new { type = "sources", sources = sourcesCompact });

            string finalAnswer = "";
            
            if (!prep.StreamAnswer)
            {
                // 直接返回
                finalAnswer = prep.ImmediateAnswer;
                await SendAsync(new { type = "answer_chunk", content = prep.ImmediateAnswer });
                await SendAsync(new { type = "answer_end", totalMs = sw.ElapsedMilliseconds, answerSource = prep.AnswerSource, hasRelevantKnowledge = prep.HasRelevantKnowledge });
            }
            else
            {
                var contextForStream = prep.AnswerSource == "direct_answer" && !string.IsNullOrEmpty(prep.DirectAnswerContext)
                    ? prep.DirectAnswerContext
                    : (string.IsNullOrEmpty(prep.Context) ? "没有找到相关的知识库信息" : prep.Context);
                await foreach (var delta in chatService.GenerateResponseStreamAsync(question, contextForStream))
                {
                    if (string.IsNullOrEmpty(delta)) continue;
                    finalAnswer += delta;
                    await SendAsync(new { type = "answer_chunk", content = delta });
                }
                sw.Stop();
                await SendAsync(new { type = "answer_end", totalMs = sw.ElapsedMilliseconds, answerSource = prep.AnswerSource, hasRelevantKnowledge = prep.HasRelevantKnowledge });
            }
            
            // 保存对话记录到数据库
            try
            {
                if (!string.IsNullOrEmpty(finalAnswer))
                {
                    var actualSessionId = await sessionService.GetOrCreateSessionAsync(sessionId, userId, "WebSocket会话");
                    var sourceDocuments = prep.KnowledgeSources.Any() ? JsonSerializer.Serialize(prep.KnowledgeSources) : null;
                    
                    var conversation = new RAG.Domain.Entities.App.Conversations
                    {
                        Id = Guid.NewGuid(),
                        Question = question,
                        Answer = finalAnswer,
                        SourceDocuments = sourceDocuments,
                        ConfidenceScore = prep.KnowledgeSources.Any() ? (decimal)prep.KnowledgeSources.Max(s => s.SimilarityScore) : null,
                        ResponseTimeMs = (int)sw.ElapsedMilliseconds,
                        UserId = userId,
                        SessionId = actualSessionId,
                        CreatedAt = DateTime.UtcNow,
                        UpdatedAt = DateTime.UtcNow,
                        CreatedBy = userId,
                        UpdatedBy = userId
                    };

                    await conversationService.SaveConversationAsync(conversation);
                    logger.LogInformation("[WS][RAG] 对话记录保存成功，ID: {ConversationId}", conversation.Id);
                }
            }
            catch (Exception ex)
            {
                logger.LogError(ex, "[WS][RAG] 保存对话记录时发生错误");
                // 不抛出异常，避免影响主要功能
            }
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "[WS][RAG] 处理问题时出错");
            await SendAsync(new { type = "error", message = ex.Message });
        }
    }
});

app.Run();
